home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Human Interface Toolbox / Fragment Tool / Lists.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  14.0 KB  |  594 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Lists.c
  3.  
  4.     Contains:    List Manager stuff and associated routines
  5.  
  6.     Written by: Chris White    
  7.  
  8.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/5/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. #include <Drag.h>
  25. #include <Errors.h>
  26. #include <MixedMode.h>
  27.  
  28.  
  29. #include "MenuStuff.h"
  30.  
  31. #include "Prototypes.h"
  32.  
  33.  
  34. static Boolean UserWantsToDrag ( ListRef theList, Point globallPt );
  35. static Boolean MouseHitSelection ( ListRef theList, Point localPt );
  36. static Boolean EqualCells ( Cell c1, Cell c2 );
  37. static void HandleClickInList ( WindowRef theWindow, Boolean newClick, 
  38.                                 Boolean doubleClick, Cell newCell );
  39. static Boolean GetSelectionRect ( ListRef theList, Rect* theRect );
  40. static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent );
  41. static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow );
  42.  
  43. static void OutlineRegion ( RgnHandle theRgn );
  44. static void LocalToGlobalRect ( Rect* theRect );
  45.  
  46. static Boolean MyClickLoop ( void );
  47.  
  48.  
  49.  
  50.  
  51.  
  52. // We'll limit its scope to this file
  53. static ListRef gCurrentClickedList = nil;
  54.  
  55.  
  56. //
  57. // Gotcha: The List Manager doesn't actually check D0, but rather simply tests the Z-bit in
  58. // the 68K processor's status register. Because this is lost during Mixed Mode, we need to
  59. // use a 68K stub routine that calls the native clickLoop and then tests DO before returning
  60. // to the List Manager. The emulator will set its Z-bit, and the List Manager will be happy.
  61. // See the ReadMe file for a more complete explanation.
  62. //
  63. void InitListClickLoop ( void )
  64. {
  65.  
  66. #ifdef powerc
  67.  
  68.     static RoutineDescriptor theListClickLoopRD =
  69.                 BUILD_ROUTINE_DESCRIPTOR ( uppListClickLoopProcInfo, MyClickLoop );
  70.     
  71.     
  72.     #ifdef powerc
  73.     #pragma options align=mac68k
  74.     #endif
  75.     
  76.     static struct LClickLoopGlue
  77.     {
  78.        long     move;                    // MOVEA.L  ClickLoopUPP, A0
  79.        short    jsr;                    // JSR  (A0)
  80.        short    tst;                    // TST.B D0
  81.        short    rts;                    // RTS
  82.        UniversalProcPtr ClickLoopUPP;    // Storage for the UPP
  83.     } LClickLoop68K = {
  84.        0x207A0008,
  85.        0x4E90,
  86.        0x4A00,
  87.        0x4E75,
  88.        (UniversalProcPtr) &theListClickLoopRD
  89.     };
  90.     
  91.     #ifdef powerc
  92.     #pragma options align=reset
  93.     #endif
  94.     
  95.     gClickLoopUPP = (ListClickLoopUPP) &LClickLoop68K;
  96.     
  97. #else
  98.     gClickLoopUPP = NewListClickLoopProc ( MyClickLoop );
  99. #endif
  100.     
  101.     return;
  102. }
  103.  
  104.  
  105.  
  106. Boolean PtInList ( Point localPt, ListRef theList )
  107. {
  108.     return PtInRect ( localPt, &(*theList)->rView );
  109. }
  110.  
  111.  
  112.  
  113. void GetListRect ( Rect* theRect, ListRef theList )
  114. {
  115.     *theRect = (*theList)->rView;
  116. }
  117.  
  118.  
  119.  
  120. void AddToList ( ListRef theList, Str255 theString )
  121. {
  122.     Rect    dataRect;
  123.     Cell    cell;
  124.     short    nuRow;
  125.     short    stringSize = sizeof(OSType);
  126.     
  127.     #if DEBUGGING
  128.     if ( theList == nil ) DebugStr ( "\p theList == nil");
  129.     #endif
  130.     
  131.     dataRect = (*theList)->dataBounds;
  132.     nuRow = LAddRow ( 1, dataRect.bottom, theList );
  133.     SetPt ( &cell, 0, nuRow );
  134.     LSetCell ( &theString[1], theString[0], cell, theList );
  135.     
  136.     return;
  137. }
  138.  
  139.  
  140.  
  141. void UpdateList ( ListRef theList, int16 theIndex, Str255 theString )
  142. {
  143.     Cell    theCell;
  144.     
  145.     SetPt ( &theCell, 0, theIndex );
  146.     LSetCell ( &theString[1], theString[0], theCell, theList );
  147.     
  148.     return;
  149. }
  150.  
  151.  
  152.  
  153. void DeleteFromList ( ListRef theList, int16 theIndex )
  154. {
  155.     LDelRow ( 1, theIndex, theList );
  156.     
  157.     return;
  158. }
  159.  
  160.  
  161. /*------------------------------------------------------------------*/
  162.  
  163.  
  164.  
  165. Boolean HandleListClick ( WindowRef theWindow, EventRecord* event )
  166. {
  167.     Boolean        bWasHandled = false;
  168.     Point        localPt;
  169.     Rect        listRect;
  170.     ListRef    theList;
  171.     
  172.     
  173.     
  174.     localPt = event->where;
  175.     GlobalToLocal ( &localPt );
  176.     theList = GetWListRef ( theWindow );
  177.     
  178.     
  179.     if ( (*theList)->lActive == true )
  180.     {
  181.         listRect = (*theList)->rView;
  182.         listRect.right += 15; listRect.bottom += 15;
  183.         
  184.         if ( PtInRect ( localPt, &listRect ) )
  185.         {    
  186.             Cell        oldCell, newCell;
  187.             Boolean        oldSelection, 
  188.                         validSelection = false, 
  189.                         newClick = false, 
  190.                         doubleClick;
  191.             
  192.             
  193.             oldSelection = GetFirstSelection ( theList, &oldCell );
  194.             
  195.             
  196.             
  197.             // UserWantsToDrag takes a point in global coords. It uses the Drag Manager
  198.             // routine WaitMouseMoved to make sure the user is dragging as opposed
  199.             // to double clicking. If a drag is started when the user is really trying
  200.             // to double click, it will end up being a very quick drag.
  201.             
  202.             if ( UserWantsToDrag ( theList, event->where ) )
  203.             {
  204.                 
  205.                 // The click loop code needs to wait until the mouse is moved out
  206.                 // of the list rect before starting a drag. Because the default
  207.                 // behaviour of a a drag within a list is to change the selection
  208.                 // and, if required, scroll the list, attempting to drag a cell
  209.                 // by moving the mouse over another cell will result in that cell
  210.                 // being hilited. The job of this code is to detect those attempted
  211.                 // drags and call the necessary drag routines instead of making a
  212.                 // call to the LClick routine.
  213.                 
  214.                 
  215.                 // If we successfully drop the cell somewhere else,
  216.                 // clear the selection in the original list.
  217.                 
  218.                 if ( StartADrag ( theWindow, event ) == noErr )
  219.                     LSetSelect ( false, oldCell, theList );
  220.                 
  221.                 return true;
  222.                 
  223.             }
  224.                         
  225.             //
  226.             // Save the current list for use in our click loop 
  227.             //
  228.             gCurrentClickedList = theList;
  229.             doubleClick = LClick ( localPt, event->modifiers, theList );
  230.             
  231.             validSelection = GetFirstSelection ( theList, &newCell );
  232.             if ( validSelection ) 
  233.                 if ( !oldSelection || !EqualCells ( newCell, oldCell ) )
  234.                     newClick = true;
  235.             
  236.             HandleClickInList ( theWindow, newClick, doubleClick, newCell );
  237.             bWasHandled = true;
  238.         }
  239.     }
  240.     
  241.     return bWasHandled;
  242. }
  243.  
  244.  
  245.  
  246. Boolean GetSelection ( ListRef theList, int16* theIndex )
  247. {
  248.     Boolean        bFound;
  249.     Cell        theCell;
  250.     
  251.     SetPt ( (Point*) &theCell, 0, *theIndex );
  252.     bFound = LGetSelect ( true, &theCell, theList );
  253.     if ( bFound )
  254.         *theIndex = theCell.v;
  255.     
  256.     return bFound;
  257. }
  258.  
  259.  
  260.  
  261. Boolean GetFirstSelection ( ListRef theList, Cell* theCell )
  262. {
  263.     SetPt ( (Point*) theCell, 0, 0 );
  264.     return LGetSelect ( true, theCell, theList );
  265. }
  266.  
  267.  
  268.  
  269. //
  270. // Does the user want to drag something? First, checks the click could
  271. // be drag, then waits to see if the user starts to drag the cell.
  272. // 
  273. static Boolean UserWantsToDrag ( ListRef theList, Point globalPt )
  274. {
  275.     Point    localPt;
  276.     
  277.     localPt = globalPt;
  278.     GlobalToLocal ( &localPt );
  279.     if ( MouseHitSelection ( theList, localPt ) )
  280.         return WaitMouseMoved ( globalPt );
  281.         
  282.     return false;
  283. }
  284.  
  285.  
  286.  
  287. //
  288. // Has the user clicked on a select cell?
  289. //
  290. static Boolean MouseHitSelection ( ListRef theList, Point localPt )
  291. {
  292.     Cell        selectedCell;
  293.     Rect        selectedCellRect;
  294.     
  295.     
  296.     if ( !(GetFirstSelection ( theList, &selectedCell )) )
  297.         return false;
  298.  
  299.     if ( !(PtInRect ( *(Point*) &selectedCell, (Rect*) &(*theList)->visible )) )
  300.         return false;
  301.  
  302.     if ( !(GetSelectionRect ( theList, &selectedCellRect )) )
  303.         return false;
  304.     
  305.     return PtInRect ( localPt, &selectedCellRect );
  306. }
  307.  
  308.  
  309.  
  310. static Boolean EqualCells ( Cell c1, Cell c2 )
  311. {
  312.     return (c1.h == c2.h && c1.v == c2.v);
  313. }
  314.  
  315.  
  316.  
  317. //
  318. // This routine is called in response to a click in a list. We'll
  319. // turn a double click into a “Get Info” command.
  320. //  
  321. static void HandleClickInList ( WindowRef theWindow, Boolean newClick, 
  322.                                 Boolean doubleClick, Cell newCell )
  323. {
  324.     #pragma unused(theWindow,newClick,newCell)
  325.     if ( doubleClick )
  326.     {
  327.         int32 menuSelection;
  328.         
  329.         menuSelection = kFragmentMenu << 16;
  330.         menuSelection += cGetInfo;
  331.         MenuDispatch ( menuSelection );
  332.     }
  333.     
  334.     return;
  335. }
  336.  
  337.  
  338.  
  339. //
  340. // This gets a rectangle for the selection in a list. It's used to
  341. // pass to the Drag Manager for user feedback.
  342. //
  343. static Boolean GetSelectionRect ( ListRef theList, Rect* theRect )
  344. {
  345.     Boolean    validSelection;
  346.     Cell    selectedCell, adjustedCell;
  347.     Rect    selectedCellRect, emptyRect = {0, 0, 0, 0};
  348.     
  349.     *theRect = emptyRect;
  350.     validSelection = GetFirstSelection ( theList, &selectedCell );
  351.     
  352.     if ( validSelection )
  353.     {
  354.         
  355.         //
  356.         // adjustedCell is needed because the current view of the list may not
  357.         // have cell (0, 0) at the top left corner. This takes the top and left
  358.         // values of the visible field and subtracts them from the selected cell.
  359.         // We end up with this cell's location in the current view honoring scroll
  360.         // bars.
  361.         //
  362.         
  363.         adjustedCell.v = selectedCell.v - (*theList)->visible.top;
  364.         adjustedCell.h = selectedCell.h - (*theList)->visible.left;
  365.         
  366.         selectedCellRect.top = (*theList)->rView.top + (adjustedCell.v * (*theList)->cellSize.v);
  367.         selectedCellRect.bottom = selectedCellRect.top + (*theList)->cellSize.v;
  368.         
  369.         
  370.         //
  371.         // Because the cell width is 32767 (see comments in CreateContentList routine),
  372.         // we need to use the view width instead of the cell width. If we don't, the Drag
  373.         // Manager will not draw anything as the user drags the cell around (because it's
  374.         // too big). Even if the Drag Manager did do any drawing, it wouldn't reflect the
  375.         // size of the cell as the user sees it.
  376.         //
  377.         
  378.         selectedCellRect.left = (*theList)->rView.left + (adjustedCell.h * (*theList)->rView.right);
  379.         selectedCellRect.right = selectedCellRect.left + (*theList)->rView.right;
  380.         
  381.         *theRect = selectedCellRect;
  382.     }
  383.     
  384.     return validSelection;
  385. }
  386.  
  387.  
  388.  
  389. //
  390. // This creates a new drag for the Drag Manager and tracks it until
  391. // the user is done. If a successful drop is made, the Drag Manager
  392. // will call our DragReceiver routine.
  393. //
  394. static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent )
  395. {
  396.     OSErr            theErr = noErr;
  397.     DragReference    theDrag;
  398.     Rect            dragBounds;
  399.     RgnHandle        dragRgn;
  400.     ListRef        theList;
  401.     ItemReference    theItem = 1;
  402.     
  403.     
  404.     if ( !gHasDragManager )
  405.         return kDragManagerNotPresent;
  406.     
  407.     
  408.     theList = GetWListRef ( theWindow );
  409.     
  410.     theErr = NewDrag ( &theDrag );
  411.     if ( theErr == noErr )
  412.     {        
  413.         theErr = AddFlavors ( theDrag, theItem, theWindow );
  414.  
  415.         if ( theErr == noErr )
  416.         {
  417.             
  418.             if ( GetSelectionRect ( theList, &dragBounds ) )
  419.             {
  420.                 LocalToGlobalRect ( &dragBounds );                
  421.                 theErr = SetDragItemBounds ( theDrag, theItem, &dragBounds );
  422.  
  423.                 if ( theErr == noErr )
  424.                 {    
  425.                     dragRgn = NewRgn ( );
  426.                     RectRgn ( dragRgn, &dragBounds );
  427.                     OutlineRegion ( dragRgn );
  428.                     theErr = TrackDrag ( theDrag, theEvent, dragRgn );
  429.                     
  430.                     DisposeRgn ( dragRgn );
  431.                 }
  432.             }
  433.         }
  434.         
  435.         DisposeDrag ( theDrag );
  436.     }
  437.  
  438.     return theErr;
  439. }
  440.  
  441.  
  442.  
  443. static Boolean MyClickLoop ( void )
  444. {
  445.     Point        localPt;
  446.     Cell        selectedCell;
  447.     WindowRef    theWindow;
  448.     ListRef    list;
  449.     
  450.     
  451.     
  452.     //
  453.     // This code starts a drag for those cases where there isn't already
  454.     // a selection, and the mouse is dragged out in such a way as to not
  455.     // cause another cell to be hilited. We need to wait until the mouse
  456.     // is dragged out of the list rect for two reasons. First, to allow
  457.     // the default behaviour to occur. Second, to stop a single click from
  458.     // acting like the current selection was dragged and release where the
  459.     // click was made. Note that the default behaviour is to change the
  460.     // selection as the mouse is dragged over the cells, and scroll the
  461.     // list if required.
  462.     // 
  463.     
  464.     
  465.     theWindow = FrontWindow ( );
  466.     list = GetWListRef ( theWindow );
  467.     
  468.     GetMouse ( &localPt );    
  469.     if ( GetFirstSelection ( list, &selectedCell ) )
  470.     {
  471.         if ( !(PtInRect ( localPt, &(*list)->rView)) )
  472.         {
  473.             EventRecord    dummyEvent;
  474.             long        tmpLong;
  475.             Rect        tmpRect;
  476.             
  477.             OSEventAvail(0, &dummyEvent);
  478.             dummyEvent.what = mouseDown;
  479.             
  480.             //
  481.             // We go through the trouble of making sure the mouse doesn't
  482.             // appear outside of the list cell region when the drag starts.
  483.             //
  484.             
  485.             GetSelectionRect(list, &tmpRect);
  486.             InsetRect(&tmpRect, 2, 2);
  487.             tmpLong = PinRect(&tmpRect, localPt);
  488.             dummyEvent.where = *(Point *) &tmpLong;
  489.             LocalToGlobal((Point *) &dummyEvent.where);
  490.             
  491.             //
  492.             // If we successfully drop the cell somewhere else,
  493.             // clear the selection in the original list.
  494.             //
  495.             
  496.             if ( StartADrag ( theWindow, &dummyEvent ) == noErr )
  497.                 LSetSelect ( false, selectedCell, list );
  498.             
  499.             return false;
  500.             
  501.         }
  502.     }
  503.  
  504.     return true;
  505. }
  506.  
  507.  
  508.  
  509. //
  510. // This tells the Drag Manager what data to send when the user drags something
  511. //
  512. static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow )
  513. {
  514.     OSErr        theErr = noErr;
  515.     int16        theIndex = 0;
  516.     Size        theSize;
  517.     ListRef        theList;
  518.     tDragData    theData;
  519.     
  520.     
  521.     // First, we'll promise to create a file if the user drags
  522.     // something out to the Finder.
  523.     theErr = AddHFSPromise ( theDrag, theItem );
  524.     if ( theErr )
  525.         return theErr;
  526.     
  527.     // Set the send procedure that will deliver any promised data.
  528.     SetDragSendProc ( theDrag, gDragSendDataProcUPP, theWindow );
  529.     
  530.     
  531.     
  532.     //
  533.     // I'm being lazy here. There's no real advantage to including
  534.     // the window reference with each item (well, apart from the effort
  535.     // saved on my part).
  536.     //
  537.     
  538.     theList = GetWListRef ( theWindow );
  539.     theData.theWindow = theWindow;
  540.     theSize = sizeof ( tDragData );
  541.     
  542.     while ( GetSelection ( theList, &theIndex ) )
  543.     {
  544.         OSErr        theErr;
  545.         
  546.         //
  547.         // The flavorSenderOnly is used to tell the Drag Manager that this data will not
  548.         // be valid in another client. Eg. If this application was launched twice, one
  549.         // could accept a drag from the other. However, since the data consists of ptrs
  550.         // rather than the actual data, it wouldn't make sense. Note, this doesn't affect
  551.         // dragging to the finder, which is a special case.
  552.         //
  553.         theData.theIndex = theIndex;
  554.         theErr = AddDragItemFlavor ( theDrag, theItem, kCreatorCode, 
  555.                                     (Ptr) &theData, theSize, flavorSenderOnly );
  556.         if ( theErr )
  557.             return theErr;
  558.             
  559.         theIndex++;
  560.     }
  561.     
  562.     
  563.     return noErr;
  564. }
  565.  
  566.  
  567.  
  568. static void OutlineRegion ( RgnHandle theRgn )
  569. {
  570.     RgnHandle tempRgn;
  571.     
  572.     tempRgn = NewRgn ( );
  573.     CopyRgn ( theRgn, tempRgn );
  574.     InsetRgn ( tempRgn, 1, 1 );
  575.     DiffRgn ( theRgn, tempRgn, theRgn );
  576.     DisposeRgn ( tempRgn );
  577.     
  578.     return;
  579. }
  580.  
  581.  
  582.  
  583. static void LocalToGlobalRect ( Rect* theRect )
  584. {
  585.     LocalToGlobal ( (Point*) &theRect->top );
  586.     LocalToGlobal ( (Point*) &theRect->bottom );
  587.     
  588.     return;
  589. }
  590.  
  591.  
  592.  
  593.  
  594.